My First PIC Project ==================== David Tait david.tait@man.ac.uk The PIC16C84 (or PIC16F84) from Microchip is a really great little processor. Being based on EEPROM (or "flash") technology means that it can be programmed in a matter of seconds and typically it can be reprogrammed around 1000 times. Of its 18 pins 13 can be used as general purpose I/O. When programmed as outputs the I/O pins are able to source 20mA and sink 25mA (more than enough to drive LEDs directly for example). It is inexpensive and can be programmed with simple DIY hardware. Obviously these features make the '84 attractive for many projects but they also mean that it is an ideal processor for anyone wanting to learn about microcontrollers. This short document is meant for people who have just built or purchased a PIC programmer and are itching to get their '84 doing something if only to convince themselves that their programmer, PIC or both are working. To do this we obviously need to lash together some simple hardware and this means knowing a little about the PIC. Here's a pinout diagram (looking from above): +-----+ +-----+ RA2 |1 +-+ 18| RA1 RA3 |2 17| RA0 RA4/T0CKI |3 16| OSC1/CLKIN /MCLR |4 16C84 15| OSC2/CLKOUT VSS |5 14| VDD RB0/INT |6 16F84 13| RB7 RB1 |7 12| RB6 RB2 |8 11| RB5 RB3 |9 10| RB4 +-------------+ The RA* and RB* pins are I/O pins associated with the PIC registers PORTA and PORTB respectively (RA4 can also be used as an input to the internal timer and RB0 can be used as an interrupt). VDD and VSS are the power pins. The '84 works over a wide range of voltages but typically VSS is connected to 0V and VDD to +5V. The main reset pin, /MCLR, can simply be tied to VDD (either directly or through a resistor) because the PIC includes a reliable power-on reset circuit - all you need to do to reset the PIC is cycle its power. The processor needs a clock and the OSC1 and OSC2 pins can be configured for a variety of different options including crystal and low cost RC oscillator modes. A simple circuit that you can use as the basis of your first PIC16C84 project is shown here: / +-O O---+---------------+--------------------+ | | | | | | +----O----+ | | + | | 14 | PIC16C84 | ------- | ____ | | ____ | --- +--[____]--O 4 16 O----+--[____]--+ ------- 1K | | | 4.7K | 4.5V --- ____ | | | | battery ------- +--[____]--O 10 | _|_ _|_ --- _|_ 470 | 5 | ___ 22pF ___ 0.1uF | - \ /^ +----O----+ | | | --- LED | | | | | | | | +-------+---------------+---------+----------+ (Grab http://www.man.ac.uk/~mbhstdj/files/test.gif for a more readable version). The circuit uses an RC oscillator and one I/O pin (RB4) attached to a LED. This is all you need to get the PIC to do something and see it happening. Charles Manning (Electronics Australia, April 1996) wrote an amazingly short (6 word) LED flasher program that you can use with this circuit: LIST P=16C84 MOVLW 0 TRIS 6 OPTION LOOP SLEEP INCF 6,F GOTO LOOP END The program is written for MPASM (Microchip's free assembler available from http://www.microchip.com). To use the program you'll need to extract it from this file using a text editor (DOS EDIT is fine), save it to another file (LIGHTS.ASM for example) then assemble it with MPASM (using the command "MPASM LIGHTS.ASM") to produce a hex file LIGHTS.HEX which can then be downloaded to the PIC using your programmer. Ignore the warnings from MPASM about TRIS and OPTION being "not recommended". Make sure you program the PIC with the watchdog enabled and the RC oscillator selected. If don't have MPASM yet here is a hex representation of the program I prepared earlier: :0C0000000030660062006300860A0328DE :00000001FF You can save these two hex records to the file LIGHTS.HEX and skip the MPASM step. If you are using one of my PIC programmers you can download this hex file with the correct configuration by using one of the following commands: PP -RW8 LIGHTS.HEX (PP V-0.3) PP -RW LIGHTS.HEX (PP V-0.4) TOPIC -RWG LIGHTS.HEX (TOPIC V-0.2) The program uses the watchdog timeout as a timing source to decide when to turn the LED on or off; in fact you can get the LED to flash at different rates by connecting it to a different bit of PORTB (RB0-RB7, pins 6-13). This is an unusual use of the watchdog. Normally the watchdog is used to make sure the PIC is behaving itself and, unless your program is specifically designed to use it, enabling the watchdog is a big mistake. The simple LIGHTS program uses the watchdog to wake it from "sleep" (i.e. power down) mode; on waking, the PIC increments the PORTB register - thus changing the states of RB0-RB7 - and promptly goes back to sleep awaiting the next watchdog timeout. The watchdog timer is clocked by an internal RC oscillator which has nominally the same period on all PICs therefore a consequence of using the watchdog for timing is that the program will still work correctly no matter what PIC oscillator configuration or frequency is actually used (well, the frequency should be at least a few kHz). This feature makes the LIGHTS program very useful for initial testing of almost any PIC protoboard. The circuit can be modified to give a slightly more entertaining effect by adding more LEDs. Connect the first LED to RB0 (pin 6), a second to RB1 (pin 7), a third to RB2 (pin 8) and so on; it's best to use at least four LEDs and you can use up to eight (the last one connected to RB7, i.e. pin 13). Each LED should be connected in series with a 470 ohm resistor and wired between the PIC pin and the -ve battery connection (VSS) just like the one in the schematic above. The following program will illuminate each LED in turn obeying a to-and-fro pattern (remember the display on the car featured in the old "Knight Rider" TV series?): LIST P=16C84 ; PORTB EQU 6 TRISB EQU 86H OPTREG EQU 81H STATUS EQU 3 CARRY EQU 0 RP0 EQU 5 MSB EQU 3 ;BIT POSITION OF LEFTMOST LED ; CLRF PORTB ;ALL LEDS OFF BSF STATUS,RP0 ;SELECT REGISTER BANK 1 CLRF TRISB^80H ;SET PORTB TO ALL OUTPUTS MOVLW 0AH MOVWF OPTREG^80H ;ASSIGN PRESCALER (1:4) TO WDT BCF STATUS,RP0 ;SELECT REGISTER BANK 0 INCF PORTB,F ;TURN ON RIGHTMOST LED BCF STATUS,CARRY ;CLEAR CARRY LEFT SLEEP ;WAIT FOR WDT TIMEOUT RLF PORTB,F ;TURN ON LED TO LEFT BTFSS PORTB,MSB ;REACHED LEFTMOST? GOTO LEFT ;LOOP IF NOT RIGHT SLEEP ;WAIT FOR WDT TIMEOUT RRF PORTB,F ;TURN ON LED TO RIGHT BTFSS PORTB,0 ;REACHED RIGHTMOST? GOTO RIGHT ;LOOP IF NOT GOTO LEFT ;START NEW CYCLE END MPASM should assemble the program to give this hex representation: :100000008601831686010A3081008312860A031056 :100010006300860D861D08286300860C061C0C28CC :020020000828AE :00000001FF Again you need to tell your programmer to enable the watchdog timer and RC oscillator. If you save the four hex records to a file (WALKLEDS.HEX say) you can download the program using my programmers by running one of these commands: PP -RW8 WALKLEDS.HEX (PP V-0.3) PP -RW WALKLEDS.HEX (PP V-0.4) TOPIC -RWG WALKLEDS.HEX (TOPIC V-0.2) As it stands the "LED walking" program is suitable for four LEDs but you can change the value of MSB if you want to use more - MSB should be 4, 5, 6 or 7 for 5, 6, 7 or 8 LEDs. The program avoids using the deprecated TRIS and OPTION instructions (Microchip don't want you to use them because they may not be supported by future PICs). Therefore, unlike the previous program, no warnings are generated when the program is assembled. To prevent MPASM generating annoying messages about the correct use of bank selection bits I have inverted the most significant bit of any bank 1 register address (e.g. I use TRISB^80H rather than simply TRISB where the "^" operator denotes bitwise exclusive-OR). This is just a trick I've picked up and there are several other ways to silence MPASM; in fact MPASM allows specific messages to be suppressed. However, I like my programs to assemble without generating warnings or messages even if many of them can be safely ignored. Getting MPASM to shut up without resorting to deliberately suppressing warnings and messages takes a little effort. As a final example, the following program will give much the same effect as the 4-LED WALKLEDS program. You'll notice that even though it does the same as the previous program this one is much longer and it's certainly not meant as an example of efficient programming. Instead it is designed to illustrate a few key PIC idioms and techniques. Amongst other things it contains an interrupt handler, routines to read and write data EEPROM, and shows how table lookups are implemented on the PIC. The program contains examples of some of the more useful MPASM features such as two kinds of macro. It also shows such things as how to override the default radix (hex) for numbers and embed PIC configuration information. Stylistically at least it looks more like a "real" PIC program. ; PATTERN.ASM ; ; A program designed to illustrate reading/writing data EEPROM ; and timer interrupts. A table of values is written to EEPROM ; and the processor then executes a "do nothing" loop. When the ; timer overflows it interrupts the processor and the next value ; in sequence is read from EEPROM then written to port B where it ; is displayed on LEDs. By changing the table any pattern of up ; to 64 values can be displayed. ; ; Copyright (C) 1997 David Tait (david.tait@man.ac.uk) PROCESSOR 16C84 __CONFIG 03FF3 ;RC oscillator PCL equ 2 STATUS equ 3 ;standard register files PORTB equ 6 EEDATA equ 8 EEADR equ 9 INTCON equ 0BH OPTREG equ 081H TRISB equ 086H EECON1 equ 088H EECON2 equ 089H RP0 equ 5 Z equ 2 GIE equ 7 T0IE equ 5 T0IF equ 2 WREN equ 2 WR equ 1 RD equ 0 #define bank0 bcf STATUS,RP0 ;select bank 0 #define bank1 bsf STATUS,RP0 ;select bank 1 magic macro ;magic EEPROM write sequence movlw 55H movwf EECON2^80H movlw 0AAH movwf EECON2^80H endm cblock 0CH ;variable block n_vals n_tmp endc ;**************************; ; Main program entry point ; ;**************************; org 0 goto start ;***********************; ; Interrupt entry point ; ;***********************; org 4 ; Normally context should be saved before the interrupt service ; routine and restored after but that's not necessary in this program ; because the processor is doing nothing between interrupts. See ; the PIC datasheet for the recommended procedure. movf EEADR,w xorwf n_vals,w btfsc STATUS,Z ;EEADR == n_vals? clrf EEADR ;yes, start again at 0 call ee_rd movf EEDATA,w ;read EEPROM movwf PORTB ;display byte incf EEADR,f ;new address bcf INTCON,T0IF ;clear interrupt flag retfie start clrf PORTB bank1 clrf TRISB^80H ;port B all outputs movlw B'00000111' movwf OPTREG^80H ;timer 0 prescale 256:1 bsf EECON1^80,WREN ;allow writing to EEPROM bank0 call ee_init ;transfer table to EEPROM bank1 bcf EECON1^80H,WREN ;disallow writing to EEPROM bank0 bsf INTCON,T0IE ;enable timer interrupt bsf INTCON,GIE ;globally allow interrupts loop goto loop ;do nothing forever ; ee_init ; ; initialise EEPROM from table ee_init clrw call lut ;get number of table entries movwf n_vals ;and save movwf n_tmp ;and again clrf EEADR decf EEADR,f ;EEADR = -1 ee_in1 incf EEADR,f ;next address movf EEADR,w addlw 1 call lut ;get associated table entry movwf EEDATA call ee_wr ;write to EEPROM decfsz n_tmp,f ;another? goto ee_in1 ;yes clrf EEADR ;no, then finished return ; lut ; ; look up table lut addwf PCL,f ;add W to PCL to get table entry retlw D'12' ;number of entries in table retlw B'1000' ;first entry retlw B'1000' retlw B'0100' retlw B'0100' retlw B'0010' retlw B'0010' retlw B'0001' retlw B'0001' retlw B'0010' retlw B'0010' retlw B'0100' retlw B'0100' ;last entry ; ee_wr ; ; Writes byte in EEDATA to EEPROM location at EEADR. Interrupts ; should be disabled before calling ee_wr. ee_wr bank1 magic ;invoke magic sequence bsf EECON1^80H,WR ;start write ee_wr1 btfsc EECON1^80H,WR ;write complete? goto ee_wr1 ;no bank0 return ; ee_rd ; ; Reads EEPROM byte at EEPROM location EEADR into EEDATA ee_rd bank1 bsf EECON1^80H,RD ;start read bank0 return ;read will be complete on return end Here is the MPASM generated hex file (save as PATTERN.HEX): :020000000E28C8 :0800080009080C060319890127 :10001000442008088600890A0B110900860183160E :10002000860107308100081583121C2083160811F1 :1000300083128B168B171B2800012C208C008D003F :1000400089018903890A0908013E2C2088003A2089 :100050008D0B22288901080082070C3408340834EB :1000600004340434023402340134013402340234DE :1000700004340434831655308900AA30890088146A :100080008818402883120800831608148312080079 :02400E00F33F7E :00000001FF With my programmers this can be downloaded using: PP PATTERN.HEX (PP V-0.4) TOPIC -G PATTERN.HEX (TOPIC V-0.2) These projects may not seem very exciting but if you have just built or bought a PIC programmer and have hurriedly put together the simple test circuit then seeing a LED flash on and off is exceedingly gratifying. I hope you find that out for yourself. Good luck. 2nd Edition 4/Feb/97